﻿using UnityEngine;
using UnityEngine.Rendering;

// CameraRenderer 是一个自定义的渲染控制器，负责对单个 Camera 执行完整的渲染逻辑
// 使用了 partial 修饰符，意味着这个类可能在其他文件中也有定义（比如分开管理不同功能模块）
public partial class CameraRenderer
{
	// 定义一个 ShaderTagId，用于标识渲染时使用的 Shader Pass 名称
	// "SRPDefaultUnlit" 是 Unity SRP 默认为 Unlit Shader 提供的 Pass 名称
	// 你可以根据你的 Shader Pass 名称修改它，比如 "UniversalForward" 等
	static ShaderTagId unlitShaderTagId = new ShaderTagId("SRPDefaultUnlit");

	// 定义一个常量字符串，作为 CommandBuffer 的名称，便于在 Profiler 中识别
	const string bufferName = "Render Camera";

	// 一个全局的 CommandBuffer，用于收集渲染指令，提升效率
	// 它的名称设置为 bufferName，方便调试和 Profiler 查看
	CommandBuffer buffer = new CommandBuffer { name = bufferName };

	// 当前渲染所用的 ScriptableRenderContext，用于提交渲染命令到 GPU
	ScriptableRenderContext context;

	// 当前正在渲染的 Camera 对象
	Camera camera;

	// 当前相机的剔除结果，通过 Culling 获取哪些物体需要被渲染
	CullingResults cullingResults;

	/// <summary>
	/// 主入口方法：执行一整个相机的渲染流程
	/// </summary>
	/// <param name="context">渲染上下文，用于提交渲染指令</param>
	/// <param name="camera">要渲染的目标相机</param>
	public void Render(ScriptableRenderContext context, Camera camera)
	{
		// 保存传入的上下文和相机引用到当前实例字段，方便后续方法使用
		this.context = context;
		this.camera = camera;

		// 1. 准备 CommandBuffer（比如设置名字等，可扩展）
		PrepareBuffer();

		// 2. 如果是在 Scene 视图中，进行一些特殊处理（比如显示摄像机边框等）
		PrepareForSceneWindow();

		// 3. 执行剔除操作：获取该相机可见的物体列表（CullingResults）
		//    如果剔除失败（比如相机参数无效），则直接退出渲染
		if (!Cull())
		{
			return;
		}

		// 4. 初始化渲染设置：比如设置摄像机属性、清除颜色/深度缓冲等
		Setup();

		// 5. 绘制可见的几何体，包括不透明物体和天空盒
		DrawVisibleGeometry();

		// 6. 绘制使用了不支持 Shader Pass 的物体（调试用，一般为空）
		DrawUnsupportedShaders();

		// 7. 绘制 Gizmos（比如在 Scene 视图中的辅助线、选中框等，仅在 Editor 下有效）
		DrawGizmos();

		// 8. 提交所有渲染指令到 GPU，并结束本次渲染流程
		Submit();
	}

	/// <summary>
	/// 执行剔除操作，获取该相机可见的对象
	/// </summary>
	/// <returns>是否剔除成功</returns>
	bool Cull()
	{
		// 尝试从相机获取剔除参数（比如视锥体、LayerMask 等）
		if (camera.TryGetCullingParameters(out ScriptableCullingParameters p))
		{
			// 执行剔除，得到可见的对象列表，存储在 cullingResults 中
			cullingResults = context.Cull(ref p);
			return true;
		}
		// 剔除参数获取失败（比如相机设置错误），不进行渲染
		return false;
	}

	/// <summary>
	/// 初始化渲染环境，比如设置摄像机属性、清除渲染目标
	/// </summary>
	void Setup()
	{
		// 告诉 SRP 当前正在为哪个相机进行渲染，设置视口、投影矩阵等
		context.SetupCameraProperties(camera);

		// 获取相机的清除标志（比如是否清除颜色、深度）
		CameraClearFlags flags = camera.clearFlags;

		// 清除渲染目标：
		// - 是否清除深度缓冲
		// - 是否清除颜色缓冲
		// - 清除颜色值（如果是清除颜色的话）
		buffer.ClearRenderTarget(
			flags <= CameraClearFlags.Depth,           // 是否清除深度
			flags <= CameraClearFlags.Color,           // 是否清除颜色
			flags == CameraClearFlags.Color ?          // 如果要清除颜色，使用相机背景色（转为线性空间）
				camera.backgroundColor.linear : Color.clear
		);

		// 开始一个采样区域，用于在 Profiler 中区分不同的渲染阶段
		buffer.BeginSample(SampleName);

		// 执行当前 CommandBuffer 中积累的指令
		ExecuteBuffer();
	}

	/// <summary>
	/// 提交渲染指令并结束本次渲染流程
	/// </summary>
	void Submit()
	{
		// 结束采样区域
		buffer.EndSample(SampleName);

		// 执行缓冲区中的命令
		ExecuteBuffer();

		// 提交所有渲染指令到 GPU，这是真正让 GPU 开始工作的关键调用！
		context.Submit();
	}

	/// <summary>
	/// 执行 CommandBuffer 中的指令，并清空缓冲区，以便下一轮使用
	/// </summary>
	void ExecuteBuffer()
	{
		// 把当前 CommandBuffer 中的指令提交给渲染上下文
		context.ExecuteCommandBuffer(buffer);

		// 清空 CommandBuffer，准备下一轮指令收集
		buffer.Clear();
	}

	/// <summary>
	/// 绘制当前相机可见的几何体，包括不透明物体和透明物体
	/// </summary>
	void DrawVisibleGeometry()
	{
		// 设置不透明物体的排序规则（比如按照摄像机距离等）
		var sortingSettings = new SortingSettings(camera)
		{
			criteria = SortingCriteria.CommonOpaque  // 常见的不透明物体排序标准
		};

		// 构造 DrawingSettings：指定使用的 Shader Pass（unlitShaderTagId）、排序规则
		var drawingSettings = new DrawingSettings(unlitShaderTagId, sortingSettings);

		// 构造 FilteringSettings：只渲染属于不透明队列范围内的物体（通常是 Queue < 3000）
		var filteringSettings = new FilteringSettings(RenderQueueRange.opaque);

		// 执行渲染：从剔除结果中绘制符合要求的渲染器（MeshRenderer 等）
		context.DrawRenderers(
			cullingResults, ref drawingSettings, ref filteringSettings
		);

		// 绘制天空盒（如果相机有设置且场景中有天空盒）
		context.DrawSkybox(camera);

		// ========== 透明物体部分 ==========

		// 修改排序规则为透明物体的排序方式（比如从远到近）
		sortingSettings.criteria = SortingCriteria.CommonTransparent;

		// 更新 DrawingSettings 中的排序设置
		drawingSettings.sortingSettings = sortingSettings;

		// 修改 FilteringSettings，只渲染透明队列范围内的物体（通常是 Queue >= 3000）
		filteringSettings.renderQueueRange = RenderQueueRange.transparent;

		// 执行渲染：绘制透明物体
		context.DrawRenderers(
			cullingResults, ref drawingSettings, ref filteringSettings
		);
	}
}